import { Component, defineAsyncComponent } from "vue";
import { getComponentSymbol } from "./constant";
import { getRandomId } from "./utils";
import { type ModuleConfig, BaseModule } from "./module";

type InitTimeString = "afterInit" | "beforeRender" | "afterRender";
type ModuleDefine = {
    module: new (...args: unknown[]) => BaseModule;
    initTime?: InitTimeString;
};
type ModuleDefineInstance = {
    module: BaseModule;
    initTime?: InitTimeString;
};
export type ComponentProps = {
    viewId: string;
};
type ComponentMap = {
    loader: () => Promise<Component>;
    loadingComponent?: () => Component;
    errorComponent?: () => Component;
};
type ResultMap<EM extends Record<string, ModuleDefine>, IM extends Record<string, ModuleDefine>> = {
    componentMap: ComponentMap;
    internalModuleProtos: IM;
    externalModuleProtos: EM;
};
type ExtractModuleConfig<M> = M extends new (...args: unknown[]) => BaseModule<infer C> ? C : never;
type ExtractModuleInstance<T extends Record<string, ModuleDefineInstance>> = {
    [K in keyof T]: T[K]["module"];
};
export type ToModuleInstanceDefine<T extends Record<string, ModuleDefine>> = {
    [K in keyof T]: {
        module: InstanceType<T[K]["module"]>;
        initTime?: T[K]["initTime"];
    };
};
export interface ViewConfig {
    [key: string]: unknown;
}
type InternalModuleConfig<M extends Record<string, ModuleDefine>> = {
    [K in keyof M]: ExtractModuleConfig<M[K]["module"]>;
};

/**
 * 视图基类
 * @param EM 外部依赖模块类型
 * @param IM 内部依赖模块类型
 * @param VC 视图配置类型
 * @param MC 内部模块配置类型
 * @param EMI 外部依赖模块实例类型
 * @param IMI 内部依赖模块实例类型
 */
export abstract class BaseView<
    EM extends Record<string, ModuleDefine> = NonNullable<unknown>,
    IM extends Record<string, ModuleDefine> = NonNullable<unknown>,
    VC extends ViewConfig = ViewConfig,
    MC extends InternalModuleConfig<IM> = InternalModuleConfig<IM>,
    EMI extends ExtractModuleInstance<ToModuleInstanceDefine<EM>> = ExtractModuleInstance<
        ToModuleInstanceDefine<EM>
    >,
    IMI extends ExtractModuleInstance<ToModuleInstanceDefine<IM>> = ExtractModuleInstance<
        ToModuleInstanceDefine<IM>
    >
> {
    protected _id: string = getRandomId();
    /** 唯一标识 */
    get id() {
        return this._id;
    }
    protected _inited: boolean = false;
    /** 是否初始化 */
    get inited() {
        return this._inited;
    }
    protected _zIndex: number = 0;
    /** 堆叠顺序 */
    get zIndex() {
        return this._zIndex;
    }
    protected _hidden: boolean = false;
    /** 是否隐藏 */
    get hidden() {
        return this._hidden;
    }
    /** 内部模块原型 */
    protected internalModuleProtos: IM = {} as IM;
    /** 内部依赖模块 */
    internalModules: IMI = {} as IMI;
    /** 外部模块原型 */
    protected externalModuleProtos: EM = {} as EM;
    /** 外部依赖模块 */
    externalModules: EMI = {} as EMI;
    /** 视图组件定义 */
    protected componentMap?: ComponentMap;
    /** 配置 */
    declare config: MC & VC;

    // eslint-disable-next-line complexity
    protected async initModules(
        state: "afterInit" | "beforeRender" | "afterRender",
        internalModuleProtos?: IM,
        externalModuleProtos?: EM,
        internalModules?: IMI,
        externalModules?: EMI,
        config?: MC & VC
    ): Promise<void> {
        const im = internalModuleProtos ?? this.internalModuleProtos ?? {};
        const em = externalModuleProtos ?? this.externalModuleProtos ?? {};
        const imi = internalModules ?? this.internalModules;
        const emi = externalModules ?? this.externalModules;
        const c = config ?? this.config!;
        const promises: Promise<BaseModule<ModuleConfig>>[] = [];
        for (const key in im) {
            const { module, initTime } = im[key];
            if (initTime === state) {
                const moduleInstance = new module();
                imi[key as keyof IMI] = moduleInstance as IMI[keyof IMI];
                promises.push(moduleInstance.init(c[key]));
            }
        }
        for (const key in emi) {
            const moduleInstance = emi[key];
            const initTime = em[key]?.initTime;
            if (initTime === state) {
                if (!moduleInstance.inited) {
                    promises.push(moduleInstance.init(moduleInstance.config));
                }
            }
        }
        await Promise.all(promises);
    }
    protected abstract _init(): Promise<ResultMap<EM, IM>> | ResultMap<EM, IM>;
    /**
     * 初始化视图
     * @returns 视图实例
     */
    async init(config: MC & VC, externalModules: EMI): Promise<void> {
        if (this._inited) {
            throw new Error("View has been initialized");
        }
        this._inited = true;
        this.config = config;
        this.externalModules = externalModules;
        const result = await this._init();
        const internalModuleProtos = result.internalModuleProtos;
        await this.initModules("afterInit", internalModuleProtos);
        this.componentMap = result.componentMap;
        this.externalModuleProtos = result.externalModuleProtos;
        this.internalModuleProtos = internalModuleProtos;
    }
    /** 获取视图组件 */
    [getComponentSymbol] = () => {
        if (!this.config) {
            throw new Error("View config not found");
        }
        if (!this._inited) {
            throw new Error("View not initialized");
        }
        if (!this.componentMap) {
            throw new Error("View already initialized, but componentMap not found");
        }
        const internalModuleProtos = this.internalModuleProtos;
        const externalModuleProtos = this.externalModuleProtos;
        const internalModules = this.internalModules;
        const externalModules = this.externalModules;
        const config = this.config;
        const initModule = this.initModules;
        const viewId = this.id;
        const AsyncComponent = defineAsyncComponent(this.componentMap);
        const WrappedComponent = {
            setup() {
                onBeforeMount(async () => {
                    await initModule(
                        "beforeRender",
                        internalModuleProtos,
                        externalModuleProtos,
                        internalModules,
                        externalModules,
                        config
                    );
                });
                onMounted(async () => {
                    //? 不知为何会丢失 externalModules，需要传入
                    await initModule(
                        "afterRender",
                        internalModuleProtos,
                        externalModuleProtos,
                        internalModules,
                        externalModules,
                        config
                    );
                });
            },
            render() {
                return h(AsyncComponent, { ...config, viewId });
            }
        };
        return WrappedComponent;
    };
    /** 销毁视图 */
    destroy(): void {
        this.componentMap = undefined;
    }
    /** 调整堆叠顺序 */
    setZIndex(zIndex: number) {
        this._zIndex = zIndex;
    }
    /** 显示 */
    show() {
        this._hidden = false;
    }
    /** 隐藏 */
    hide() {
        this._hidden = true;
    }
}
